#!/bin/sh /etc/rc.common
# Copyright (C) 2011 OpenWrt.org
# Copyright (C) 2011 Linus Lüssing
#  Based on Jo-Philipp Wich's OpenVPN init script
# This is free software, licensed under the GNU General Public License v2.
# See /LICENSE for more information.

START=42

SERVICE_USE_PID=1

BIN=/usr/sbin/tincd
if ( type extra_command >/dev/null 2>&1 ); then
        extra_command "up" "<instance> Setting instance up"
        extra_command "down" "<instance> Setting instance down"
else
	EXTRA_COMMANDS="up down"
fi

LIST_SEP="
"
#TMP_TINC="/tmp/tinc"

append_param() {
	local v="$1"
	case "$v" in
		*_*_*_*) v=${v%%_*}-${v#*_}; v=${v%%_*}-${v#*_}; v=${v%%_*}-${v#*_} ;;
		*_*_*)   v=${v%%_*}-${v#*_}; v=${v%%_*}-${v#*_} ;;
		*_*)     v=${v%%_*}-${v#*_} ;;
	esac
	ARGS="$ARGS --$v"
	return 0
}

#append_conf_bools() {
#	local p; local v; local s="$1"; local f="$2"; shift; shift
#	for p in $*; do
#		config_get_bool v "$s" "$p"
#		[ "$v" == 1 ] && echo "$p = yes" >> "$f"
#		[ "$v" == 0 ] && echo "$p = no" >> "$f"
#	done
#}
append_conf_bools() {
    local p; local v; local s="$1"; local f="/etc/tinc/$s/tinc.conf"; shift; shift
    for p in $*; do
		config_get_bool v "$s" "$p"
		[ "$v" == 1 ] && echo "$p = yes" >> "$f"
		[ "$v" == 0 ] && echo "$p = no" >> "$f"
	done
}


append_params() {
	local p; local v; local s="$1"; shift
	for p in $*; do
		config_get v "$s" "$p"
		IFS="$LIST_SEP"
		for v in $v; do
			[ -n "$v" ] && append_param "$p" && ARGS="$ARGS=$v"
		done
		unset IFS
	done
}

#append_conf_params() {
#	local p; local v; local s="$1"; local f="$2"; shift; shift
#	for p in $*; do
#		config_get v "$s" "$p"
#		IFS="$LIST_SEP"
#		for v in $v; do
#			# Look up OpenWRT interface names
#			[ "$p" = "BindToInterface" ] && {
#				local ifname=$(uci_get_state network "$v" ifname "")
#				[ -n "$ifname" ] && v="$ifname"
#			}
#
#			[ -n "$v" ] && echo "$p = $v" >> "$f"
#		done
#		unset IFS
#	done
#}
append_conf_params() {
    local p; local v; local s="$1"; local f="/etc/tinc/$s/tinc.conf"; shift; shift
    for p in $*; do
               config_get v "$s" "$p"
               IFS="$LIST_SEP"
               for v in $v; do
                       # Look up OpenWRT interface names
                       [ "$p" = "BindToInterface" ] && {
                               local ifname=$(uci_get_state network "$v" ifname "")
                               [ -n "$ifname" ] && v="$ifname"
                       }

                       [ -n "$v" ] && echo "$p = $v" >> "$f"
               done
               unset IFS
       done

}
section_enabled() {
	config_get_bool enabled "$1" 'enabled' 0
	[ $enabled -gt 0 ]
}

#prepare_host() {
#	local s="$1"
#	local n
#
#	# net disabled?
#	config_get n "$s" net
#	section_enabled "$n" || return 1
#
#	if [ "$#" = "2" ]; then
#		[ "$2" != "$n" ] && return 1
#	fi
#
#	HOST_CONF_FILE="$TMP_TINC/$n/hosts/$s"
#	MANDATORY_PARAM_IN_UCI=0
#	[ ! -f "/etc/tinc/$n/hosts/$s" ] && {
#		config_get pk_i "$s" "PublicKey"
#		config_get pk_f "$s" "PublicKeyFile"
#		config_get pked_i "$s" "Ed25519PublicKey"
#		config_get pked_f "$s" "Ed25519PublicKeyFile"
#		config_get na "$s" "Name"
#		if [ -n "$na" ] ; then
#			HOST_CONF_FILE="$TMP_TINC/$n/hosts/$na"
#		fi
#		if [ -n "$pk_i$pk_f$pked_i$pked_f" ] ; then
#			MANDATORY_PARAM_IN_UCI=1
#		fi
#	}
#
#	# host disabled?
#	section_enabled "$s" || {
#		[ -f "$HOST_CONF_FILE" ] && rm "$HOST_CONF_FILE"
#		return 1
#	}
#
#	[ ! -f "/etc/tinc/$n/hosts/$s" ] && {
#		if [ "$MANDATORY_PARAM_IN_UCI" -eq 1 ] ; then
#			touch "$HOST_CONF_FILE"
#		else
#			echo -n "tinc: Warning, public key for $s for network $n "
#			echo -n "missing in /etc/tinc/$n/hosts/$s, "
#			echo "skipping configuration of $s"
#			return 1
#		fi
#	}
#
#	# append flags
#	append_conf_bools "$s" "$HOST_CONF_FILE" \
#		ClampMSS \
#		IndirectData \
#		PMTUDiscovery \
#		TCPOnly
#
#	# append params
#	append_conf_params "$s" "$HOST_CONF_FILE" \
#		Address \
#		Cipher \
#		Compression \
#		Digest \
#		Ed25519PublicKey \
#		Ed25519PublicKeyFile \
#		MACLength \
#		PMTU \
#		Port \
#		PublicKey \
#		PublicKeyFile \
#		Subnet
#}
prepare_host() {
    local s="$1"
    local n

    # net disabled?
    config_get n "$s" net
    section_enabled "$n" || return 1

    if [ "$#" = "2" ]; then
        [ "$2" != "$n" ] && return 1
    fi

    HOST_CONF_FILE="/etc/tinc/$n/hosts/$s"
    MANDATORY_PARAM_IN_UCI=0

    # Check if the host configuration file exists in /etc/tinc
    [ ! -f "$HOST_CONF_FILE" ] && {
        # Fetch the necessary keys and parameters from UCI
        config_get pk_i "$s" "PublicKey"
        config_get pk_f "$s" "PublicKeyFile"
        config_get pked_i "$s" "Ed25519PublicKey"
        config_get pked_f "$s" "Ed25519PublicKeyFile"
        config_get na "$s" "Name"

        # Use the 'Name' parameter if available as the filename
        if [ -n "$na" ]; then
            HOST_CONF_FILE="/etc/tinc/$n/hosts/$na"
        fi

        # Check if any of the key parameters are set in UCI
        if [ -n "$pk_i$pk_f$pked_i$pked_f" ]; then
            MANDATORY_PARAM_IN_UCI=1
        fi
    }

    # host disabled?
    section_enabled "$s" || return 1


    # If the host configuration file does not exist in /etc/tinc, create it if mandatory parameters are set in UCI
    [ ! -f "$HOST_CONF_FILE" ] && {
        if [ "$MANDATORY_PARAM_IN_UCI" -eq 1 ]; then
            touch "$HOST_CONF_FILE"
        else
            echo -n "tinc: Warning, public key for $s for network $n "
            echo -n "missing in $HOST_CONF_FILE, "
            echo "skipping configuration of $s"
            return 1
        fi
    }

    # append flags
    append_conf_bools "$s" "$HOST_CONF_FILE" \
        ClampMSS \
        IndirectData \
        PMTUDiscovery \
        TCPOnly

    # append params
    append_conf_params "$s" "$HOST_CONF_FILE" \
        Address \
        Cipher \
        Compression \
        Digest \
        Ed25519PublicKey \
        Ed25519PublicKeyFile \
        MACLength \
        PMTU \
        Port \
        PublicKey \
        PublicKeyFile \
        Subnet
}


#check_gen_own_key() {
#	local s="$1"; local n; local k
#
#	config_get n "$s" Name
#	config_get_bool k "$s" generate_keys 0
#	[ "$k" == 0 ] && return 0
#
#	([ -z "$n" ] || [ -f "$TMP_TINC/$s/hosts/$n" ] || [ -f "$TMP_TINC/$s/rsa_key.priv" ]) && \
#		return 0
#	[ ! -d "$TMP_TINC/$s/hosts" ] && mkdir -p "$TMP_TINC/$s/hosts"
#
#	config_get k "$s" key_size
#	if [ -z "$k" ]; then
#		$BIN -c "$TMP_TINC/$s" generate-keys </dev/null
#	else
#		$BIN -c "$TMP_TINC/$s" generate-keys "$k" </dev/null
#	fi
#
#	[ ! -d "/etc/tinc/$s/hosts" ] && mkdir -p "/etc/tinc/$s/hosts"
#	cp "$TMP_TINC/$s/rsa_key.priv" "/etc/tinc/$s/"
#	[ -n "$n" ] && cp "$TMP_TINC/$s/hosts/$n" "/etc/tinc/$s/hosts/"
#}
check_gen_own_key() {
    local s="$1"; local n; local k

    config_get n "$s" Name
    config_get_bool k "$s" generate_keys 0

    [ "$k" == 0 ] && return 0

    ([ -z "$n" ] || [ -f "/etc/tinc/$s/hosts/$n" ] || [ -f "/etc/tinc/$s/rsa_key.priv" ]) && \
        return 0

    # Create hosts directory if it doesn't exist
    [ ! -d "/etc/tinc/$s/hosts" ] && mkdir -p "/etc/tinc/$s/hosts"

    config_get k "$s" key_size

    if [ -z "$k" ]; then
        $BIN -c "/etc/tinc/$s" generate-keys </dev/null
    else
        $BIN -c "/etc/tinc/$s" generate-keys "$k" </dev/null
    fi

}


#prepare_net() {
#	local s="$1"
#	local n
#
#	section_enabled "$s" || return 1
#
#	[ -d "$TMP_TINC/$s" ] && rm -rf "$TMP_TINC/$s/"
#	mkdir -p "$TMP_TINC/$s/hosts"
#	[ -d "/etc/tinc/$s" ] && cp -r "/etc/tinc/$s" "$TMP_TINC/"
#
#	# append flags
#	append_conf_bools "$s" "$TMP_TINC/$s/tinc.conf" \
#		AutoConnect \
#		DecrementTTL \
#		DeviceStandby \
#		DirectOnly \
#		ExperimentalProtocol \
#		Hostnames \
#		LocalDiscovery \
#		PriorityInheritance \
#		StrictSubnets \
#		TunnelServer \
#		ClampMSS \
#		IndirectData \
#		PMTUDiscovery \
#		TCPOnly
#
#	# append params
#	append_conf_params "$s" "$TMP_TINC/$s/tinc.conf" \
#		AddressFamily \
#		BindToAddress \
#		BindToInterface \
#		Broadcast \
#		BroadcastSubnet \
#		ConnectTo \
#		Device \
#		DeviceType \
#		Ed25519PrivateKeyFile \
#		Forwarding \
#		Interface \
#		ListenAddress \
#		LocalDiscoveryAddress \
#		Mode \
#		KeyExpire \
#		MACExpire \
#		MaxConnectionBurst \
#		Name \
#		PingInterval \
#		PingTimeout \
#		PrivateKey \
#		PrivateKeyFile \
#		ProcessPriority \
#		Proxy \
#		ReplayWindow \
#		UDPRcvBuf \
#		UDPSndBuf \
#		Address \
#		Cipher \
#		Compression \
#		Digest \
#		MACLength \
#		PMTU \
#		Port \
#		PublicKey \
#		PublicKeyFile \
#		Subnet \
#		Weight
#
#	check_gen_own_key "$s" && return 0
#}
prepare_net() {
    local s="$1"

    section_enabled "$s" || return 1


    # append flags
    append_conf_bools "$s" "/etc/tinc/$s/tinc.conf" \
        AutoConnect \
        DecrementTTL \
        DeviceStandby \
        DirectOnly \
        ExperimentalProtocol \
        Hostnames \
        LocalDiscovery \
        PriorityInheritance \
        StrictSubnets \
        TunnelServer \
        ClampMSS \
        IndirectData \
        PMTUDiscovery \
        TCPOnly

    # append params
    append_conf_params "$s" "/etc/tinc/$s/tinc.conf" \
        AddressFamily \
        BindToAddress \
        BindToInterface \
        Broadcast \
        BroadcastSubnet \
        ConnectTo \
        Device \
        DeviceType \
        Ed25519PrivateKeyFile \
        Forwarding \
        Interface \
        ListenAddress \
        LocalDiscoveryAddress \
        Mode \
        KeyExpire \
        MACExpire \
        MaxConnectionBurst \
        Name \
        PingInterval \
        PingTimeout \
        PrivateKey \
        PrivateKeyFile \
        ProcessPriority \
        Proxy \
        ReplayWindow \
        UDPRcvBuf \
        UDPSndBuf \
        Address \
        Cipher \
        Compression \
        Digest \
        MACLength \
        PMTU \
        Port \
        PublicKey \
        PublicKeyFile \
        Subnet \
        Weight

    check_gen_own_key "$s" && return 0
}

start_instance() {
	local s="$1"

	section_enabled "$s" || return 1

	ARGS=""

	# append params
	append_params "$s" logfile debug

	SERVICE_PID_FILE="/var/run/tinc.$s.pid"
	service_start $BIN -c "/etc/tinc/$s" $ARGS --pidfile="$SERVICE_PID_FILE"
}

stop_instance() {
	local s="$1"

	section_enabled "$s" || return 1

	SERVICE_PID_FILE="/var/run/tinc.$s.pid"
	service_stop $BIN

}

reload_instance() {
	local s="$1"

	section_enabled "$s" || return 1

	SERVICE_PID_FILE="/var/run/tinc.$s.pid"
	service_reload $BIN
}

start() {
	config_load 'tinc'

	config_foreach prepare_net 'tinc-net'
	config_foreach prepare_host 'tinc-host'

	config_foreach start_instance 'tinc-net'
}

stop() {
	config_load 'tinc'
	config_foreach stop_instance 'tinc-net'
}

reload() {
	config_load 'tinc'
	config_foreach reload_instance 'tinc-net'
}

up() {
	local exists
	local instance
	config_load 'tinc'
	for instance in "$@"; do
		config_get exists "$instance" 'TYPE'
		if [ "$exists" == "tinc-net" ]; then
			prepare_net "$instance"
			config_foreach prepare_host 'tinc-host' "$instance"
			start_instance "$instance"
		fi
	done
}

down() {
	local exists
	local instance
	config_load 'tinc'
	for instance in "$@"; do
		config_get exists "$instance" 'TYPE'
		if [ "$exists" == "tinc-net" ]; then
			stop_instance "$instance"
		fi
	done
}
